package com.github.baichuan.binary;


import lombok.NonNull;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

/**
 * 字节数组操作对象
 */
public class Bytes {
    public static final Bytes EMPTY = new Bytes(new byte[0]);

    private final byte[] array;

    public Bytes(@NonNull byte[] array){
        this.array = array;
    }

    public byte[] bytes(){
        return array;
    }

    public static Bytes of(@NonNull byte[] bytes) {
        return new Bytes(bytes);
    }

    public static Bytes of(String hex) {
        try{
            byte[] array = Hex.decodeHex(hex);
            return new Bytes(array);
        }
        catch (DecoderException ex){
            throw new RuntimeException(ex);
        }
    }

    /**
     * 从start位置开始截取内容到结尾
     * @param start
     * @return
     */
    public Bytes slice(int start){
        return slice(start, array.length);
    }

    /**
     * 按字节对数据内容进行切片
     * @param start 开始字节位置(包含)
     * @param end 结束字节位置(不包含)
     * @return
     */
    public Bytes slice(int start, int end){
        int startIndex = start >= 0 ? start : this.array.length + start;
        int endIndex = end >= 0 ? end : this.array.length + end;

        if(startIndex > endIndex){
            throw new IllegalArgumentException("not support slice from " + start + "" + end);
        }

        byte[] dst = new byte[endIndex - startIndex];
        for(int i = 0; i < dst.length; i++){
            dst[i] = array[startIndex + i];
        }
        return new Bytes(dst);
    }

    /**
     * 裁剪低地址字节
     */
    public Bytes leftTrim(byte trim) {
        if (array.length == 0) {
            return this;
        }

        int trimIndex = -1;
        for (int i = 0; i < array.length; i++) {
            if (array[i] != trim) {
                trimIndex = i;
                break;
            }
        }

        if (trimIndex == -1) {
            return EMPTY;
        }

        if (trimIndex == 0) {
            return this;
        }

        byte[] result = new byte[array.length - trimIndex];
        for (int i = 0; i < result.length; i++) {
            result[i] = array[i + trimIndex];
        }
        return new Bytes(result);
    }

    /**
     * 裁剪高地址字节
     */
    public Bytes rightTrim(byte trim) {
        if (this.array.length == 0) {
            return this;
        }

        int trimIndex = -1;
        for (int i = this.array.length - 1; i >= 0; i--) {
            if (this.array[i] != trim) {
                trimIndex = i;
                break;
            }
        }

        if (trimIndex == -1) {
            return EMPTY;
        }

        if (trimIndex == this.array.length - 1) {
            return this;
        }

        byte[] dst = new byte[trimIndex + 1];
        for (int i = 0; i <= trimIndex; i++) {
            dst[i] = this.array[i];
        }
        return new Bytes(dst);
    }

    /**
     * 裁剪字节数组两端的内容
     */
    public Bytes trim(byte trim){
        if (this.array.length == 0){
            return this;
        }

        return leftTrim(trim).rightTrim(trim);
    }

    /**
     * 使用0x00在右侧填充字节数组到指定长度
     * @param targetLength
     * @return
     */
    public Bytes rightPadding(int targetLength){
        return rightPadding(targetLength, (byte)0x00);
    }

    /**
     * 使用paddingByte在右侧填充字节数组到指定长度
     * @param targetLength
     * @param paddingByte
     * @return
     */
    public Bytes rightPadding(int targetLength, byte paddingByte){
        if(array.length > targetLength){
            throw new IllegalStateException("bytes length=" + array.length + " is more big than targetLength=" + targetLength);
        }

        if(array.length == targetLength){
            return this;
        }

        byte[] dst = new byte[targetLength];
        for (int i = 0; i < targetLength; i++) {
            if(i < array.length){
                dst[i] = array[i];
            }
            else{
                dst[i] = paddingByte;
            }
        }
        return new Bytes(dst);
    }

    /**
     * 翻转数据内容
     * @return
     */
    public Bytes reverse(){
        int length = array.length;
        byte[] dst = new byte[length];
        for (int i = 0; i < length; i++) {
            dst[i] = array[length - i - 1];
        }
        return new Bytes(dst);
    }
}
